///////////////////////////////////////////////////////////////
//  Copyright Christopher Kormanyos 2021.
//  Copyright 2021 John Maddock. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt
//
// This work is based on an earlier work:
// "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations",
// in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469

#ifdef _MSC_VER
#define _SCL_SECURE_NO_WARNINGS
#endif

#include <cmath>

#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/cbrt.hpp>
#include <boost/detail/lightweight_test.hpp>
#include "test.hpp"

#if defined(TEST_CPP_DEC_FLOAT)
#include <boost/multiprecision/cpp_dec_float.hpp>
#elif defined(TEST_CPP_BIN_FLOAT)
#include <boost/multiprecision/cpp_bin_float.hpp>
#else
#error ERROR!!: Backend type must be specified as cpp_dec_float or cpp_bin_float
#endif

#if defined(TEST_CPP_DEC_FLOAT)
using big_float_type =
   boost::multiprecision::number<boost::multiprecision::cpp_dec_float<10001, std::int32_t, std::allocator<void>>,
                                 boost::multiprecision::et_off>;
#elif defined(TEST_CPP_BIN_FLOAT)
using big_float_type =
   boost::multiprecision::number<boost::multiprecision::cpp_bin_float<10001>,
                                 boost::multiprecision::et_off>;
#endif

static const std::string str_control_sqrt_pi
{
   // N[Sqrt[Pi], 10003]
   "1."
   "77245385090551602729816748334114518279754945612238712821380778985291128459103218137495065673854466541622682362428257066623615286572442260252509370960278706846203769865310512284992517302895082622893209537926796280017463901535147972051670019018523401858544697449491264031392177552590621640541933250090639840761373347747515343366798978936585183640879545116516173876005906739343179133280985484624818490205465485219561325156164746751504273876105610799612710721006037204448367236529661370809432349883166842"
   "42138457096091204204277857780686947665700052183056851254133966369446541815107166938833219429293570622688652244205421499480499207564863988748385059306402182140292858112330649789452036211490789622873894032459781985131348712665125062932600446563821096750268124969305954204615607619522173915250702077927580990543329006622230676144696612481887430699788352050614644438541853079735742571791856359597499599522638492422038891039664064472939728413450430021405642334330392617561341763363200170376541634763206692"
   "76541812835762490326904508485320134192435989730871193799482938730111262561658818884785977875963761363218634246546641333954355703201522654193952186030497310513829498439659165614245955421226615102478536098095510395600789402188099613382854025016800745802729119366425192820510001936350073914643295493433951928853735459200563766502880540575532123189009126322819150914980836695624483100852221923973646324842863261145766932425371577377894414090544573595351225626391080239236909732127905807617134603914574791"
   "87979412485021844514581134188888041322095533218464670972749102856526270784545326222784880098238583630075495095476406237708338835722543662156748132766838424497242087451616183320507799148018466681423669365190284546385761482785703777438837629747998273770543158368241099868322850380552635536972229313380526442841037231204396700430761245413831179227827536371559839837688453702784298570709051122384053677901338541458531620807304313806973998743669316601381707927205604195488285806309311163629704786781402696"
   "32729627012261359858977545052894831130166840015320748519824024633375558517135680193412289759807195687402505715021417837925436430303659282112509258806189031170745431279039535536606826110011889657420487275939199769955383521150866962555964413705038292449535903106362345305647171162168587254586874400296117579217231905540571986817275884190896496579066965156017283514829038565511698072107953309161308435985243894654406821655003275379960238665037988864815211865799958571865637751133159747535960434137766451"
   "19143460134292508116324806409073773212629335747472967679341271602966512080989809057799660305102181625557978907487076211076238026267854297015027109153504985351492390832484280828987595575678848892608420885521269510357370208661259115541320440373560864338837123962064293902378666311632616788841922798194995240394245784220443030420430420710969273392946085104969289739161855607837870336428902342932718872968029721581659426129728366395905041130374745743509749058016326916537576909810974856253778503428799421"
   "92237718584431832793731298006587036861923010217454505203084553303861950660236433411532059741224264265354216558150036472691946382544124557259473900719227187097960549897246028226024291050774020812375674807627637743706345046314773769480331683570762058749578173350996762873387396702968819066142614457371322822803935444608377601058646738943855776887203971767059273166300903112608144884308624114292247965454026163755158658735198437217550552729506415043576875838149359401055659121503770441973880370568097143"
   "46316360543413650766999757621198118425874225208140436406211723091974394552459677817421914643335067146364622594792319497003496602563521033780660380527031652454546473640620830298273494336728477353020224705290099569815670586403988563683495649528828785196447555742560145992685258185630853805815521997029234802948217957003929547687673787467561474545052585171167541520282662279997703869334225001229773162123949246270758331680646407392937845782141854983327867648187925679501328451126001547882983039709412279"
   "55902739831214814542270184266024781981043333772958400258388865676834533309029610949987493741347827363468214661024873730434936857350647979436639913666636603319139931587665621516162917711861823216570008465885793874989357083653900514427893078319984999817593256144124629695956174126257098794338275338500025352194488355589261749748777846288722492925959751808959292006560416637867369908737627136240495219624777863935793728863172669947043866416226163725769669214230953364155781068802438762255656909570411119"
   "29329419336352138037692136273833819830244199702433009694861871085709025954822085641199104101975680270188980749875221764846725695738526974653312988267258169188864563262749529655208476443369725290231293888886172298365388606288519962105545717369008144991518103776264256672840522144402197474662350151152362302177635204375528607776725412022934171898265399118147904685922433784252070116245217876908741620845115578154869546824111382745732829225060159722322499657545023166289561501837132083717115348063673894"
   "84784931733912365683566355325605210062481299782871796389101124979761290941248031177301331861287374931225016243776603184702614422553084037673001012318502465258727480230169728594169868719593244190651723315464820772603457688308424446360645013191255421752939510434522411080231562909169615412193930561011909418881134283861918284802421983092749258272200999073585007397309735049927566442729112512571291910898288904993996164103611931626599002802954273008381997697092303742727834454911765521727836256287601109"
   "32255166208473363670412549865951932837423711585439714475260616254908137088745696670626628429325335237376819359653018249154079718880795628014895902440140901732999360367466419259926341267462502903084686933106760552768358294991902944382941262207244383387383548710992639420375250818632065102216297040032248061383121651896089049818986478224426012401989866131191974000724370814801426098855240714118230250496450467294859726495929559243052613611357840645183272467390330059195315273771949648585941670957079135"
   "71208139601650594925038121644613819126162372924247652609612531066175456224775667644691793111255733602951238632893409556672518588271987281331623238463434788117754723046679700987197077876381886561795342566290582176594347588749226787782077620870028951701108941253050356669850986454100131214591137167354223024474855215031201885575271538221316878310661190616431479234818598289682964444446955675311396049275406706727731378970529259539526600249211331795365053974029709734540190671438970125900686013617665195"
   "72687973099508055015643127762385528631050133875254380128109439190154705505999016058166069149685688494423898119657391772817914836040519357475938801038970500508903809193141729096407992797218919755568891010603646639884446522998359060486644235315895267693359444400323126004223165102112972082074042502545061418356462067134650264595901028481832509792442369881199067775187248038964466592173037308569206019569002906903553691216483666182982442288272244187675860189834556893273733860435288050615748230733969445"
   "78474186530443308066397549429129132930287814868929519508417573058826618191732980441004823285545754081618814817035570021561095057968747348503333241797516372109425551635208880511652501824782287983124319365126912208588242222571057432428595460772815216094389152993482877724386484010334934164007837418595795609461194089041646498368598284869636334697311192787059989803426741803667268274124973752207354967929168154574421280242660244525949811268984027714993375818153211296815116998257206433839865521131667998"
   "74512421646225811715024003745050832887130969530089731910093749817808392479440994244361969834871331949567903534204867346327056330005149544805112071035184999837985308493647201573304757781804018320982970610322298262945817088234849408510513828178436106441430254484968445676566840061568603570598998353863859883837419232118812833359937727090858195866745019480954611611719835213422118817496211290873149331743041280454519097165935298512044878386738845240577474305934119341688461016838983599740017586415986514"
   "51965095819944262457264453202270514559808838737526755650009008351031467134854677665539462037409247069039645130697247550595241566609004807391855534514872365159293944313924460101335463214967481524634943153469869484903659475912163559450595979781245452991730567329359129587659197544325565931786590196906663660605545155793513382316688065094804799038912675532718180916008233951288936444383999718074935106457889746868054217381258669110448983865029170831101859533263398090772529353443250670518769803568857168"
   "81835780060242683956246672119403771630768027385648763921020087919307224817553753988989799661812800625463458080710413482016553607229817002580132804265299140542713714123053939394621458358275171332093085623946345810925244720567080038158311521284798948447278515121112424067234765368019140869005781922146420504119187767393112983163459560389549526407730300520215721662742451140651602410469652635646458070101544719747869732605487813151166471073804854444267443665951138325602700664792462223823426368037830362"
   "27148019393536469444732501183428218507918036245748038005705684585804838509534865536011973735674935924281652500573775492013522068651524096611313931230523448030488329336880235249613516999100262182701401504389238091694167813318978739897687026397995882771057905474944084757566412920735280686916139219650606013122253782257621358161138006091059023485172621478918566759728124236443373302790962384533760569520986820661689101431363706295375429013500518704424423442146446635034690739595018396356194261104794702"
   "87456163704731907717663415135937863802506788792267061610451079193210848607297617511818706631398952702233269765053328557068418415112262788617649292213008542622876443659388901497338772639102584246387768685926405110265670628494557418283207462950444098917779203101595858810344307567330365427189853237793572718692349471296873907788505052029451976937216634259196159341629632641232237930653691385003947028744939958011043046386152534477079048794401892421173982979396566405475605645350343613825952457743905288"
   "66"
};

static const std::string str_control_pisot
{
   // Consider a fascinating near-integer number with a lot of zeros,
   // a so-called, Pisot number.

   // N[((2/(27 + 3 Sqrt[69]))^(1/3) + (1/3) ((27 + 3 Sqrt[69])/2)^(1/3))^30000, 10003]
   // The exact page at Wolftam Alpha that can calculate this number is:
   // https://www.wolframalpha.com/input/?i=N%5B%28%282%2F%2827+%2B+3+Sqrt%5B69%5D%29%29%5E%281%2F3%29+%2B+%281%2F3%29+%28%2827+%2B+3+Sqrt%5B69%5D%29%2F2%29%5E%281%2F3%29%29%5E30000%2C+10003%5D

   "5."
   "04316596065665493215059469242757481077610274680893503167844467712875718226486210610398076581934068275385782540821330947870035477787176694685987718254576565515786375180403084108087720197360263045377599299252600787464343711547834066265665424226316713797047087646002867659572088255372994141399119295425729390551598734162316493172898778929854821816913956381635362412821599451025319364903651110221515233814364527904893091337135424027258899192161986823235528295581059873200107875550497563219302434498746973"
   "20814894295138750508150895446761509382533298841076293708877836604655306868577285538664595048264681443930199901657433773640659222556791170905179712854907731191277888814410873665970386217401864110691757499520236594196403021789595612659015207824257477907567818590472521187834397950104895326861429034026538474079051464877355002168754758452522250419043608706810782792271044572273623687966932417337841611669591984103046776829649194090438705230027320037702374804434310323316910942614411443802983487519201990"
   "59562790511888049750265002682883521913896858496289686123578807667253851478579344449553914415375194372851217961223158428624231899492121175279814486360858048056575120864817906982359465546791033345570306144306889182376593558188718231183977511187240921162728629126713811608658037408930071516876158725341127622968866605210362611399728215256794224599912431852531227205445323135206226637942724364187507359956181911218068991795777326796261608752343775804668422381361446090681588594231684449905631308569143981"
   "61104641435976464505827310532042357277805504151076128744133166097040034356155594355699310372567007362846011243924380051721179466481515865177147916094265318673753576167289982789156765819918501461470394696987900055057821804367427737785530796529257310070440503552865770656588911341336781719293479609751349965199241672156885820419323697826495675216785846761301665051497508373554704845376808917106579182046296637409148513289873712928793514345917581372183082138176837444493669698475749155502722721819149673"
   "73068945254294933017249747634880287892338097288098981994162698166278766445393167015463543161066099447878202204982468188542002904684676309826229889306798208587300602722354767354500448057391481009249110193387058874499065448114445643788142392205726681802780327973470136681753346929095906271088971584210825489831313413876761278854904865782491262488363942031887926523248561357047739970904671044807781324238352577584241803357451705736527054197950691951908858510985383906254985074673094196111657808386987133"
   "10344086360548451070819222570296482527242703056995074918767257414142822271890334717277643954342541859509907472906259527629621846811719367901786024179895120064929613306517810763374494997293082055502432076618464586027616119815676773736360189991491957175057346133494837930827478266538261093888716461076377076699523221210638410121119502728028192147321213391939138517117401805454360045347532235299958021923292027677884065299422286086252105068056807793476844454728320229479895050738642250650424537785366800"
   "82426933681881662317694955672636170867065177268568641882560696136399378938092363061534206649910677121667830726528065065115430787822086187067816894109743170406015960678013961777060204838325510730464182561678677866776664114588829507728845970964346546857260085067375205588321834061968066477136954754650582659722182787527038013350885360773847158970095196369406644986845955931896822762457057294441289666846719625035790435931009430361335058057283719525424750533492243401220689154316788170873900888499149058"
   "82791350768045506828033944542308221148888187376496354399413641511521268300533409275323047144881568429552172625312395819678941865586820107952918459296302076757812530000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
   "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
   "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
   "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000130894"
   "81144651747951236035489454929072510241720588227917512706315775959573028069494819317749753909502880822522513852251922077408850982456907715693706849952685092996714802140486783196224591724342709356982736044367860439732958936561891275647223613057765646621093148215162220607946164242694428625785134442405595081929385908408997293032313197351343122456184929825694410538985099005649944281269485885003854631983177652291928247833224724739470073685255628874084632409328244867790072740306641208410784729145026418"
   "39979089421596194425472165578935561066171415528986970540137110968042622101039687541512504650792127611653419022003559870999707092195268658469247447023020525845219500954469609653377662845171315396632622078093863467489530686087070384206729526376552079363297449193397802413100986722167407526420829915123927261615512384940124596832534371123301398663197780250502055058559468959359270528574014858166221903008468713118704341049404460009686327493935610433214782810024753045226307991672445787675574109694262312"
   "11145499426482553852623279261922068531320965208068159173874513535948741238646443865707546944537551630947807901099480889585312592101994045117110058723564574602784905031701397065185758292341410282766048237930128673558313294127312232986141003880964672012780620154659135043376705292119911497020913371656546652080631096962434428853407463428964047028385461450774375621283530438935701361719555114064935507016351711366822390479821669249539058602250563907802883915793827509214138670896771346930036795134153954"
   "12614845274332289375572914965666103280434189995736578368489983718312997135507213909760008434894911666727883219968251092951782420006186638047586450238092418753928237145956209661039483486342922895231063933556593966455655337130545768190919525316022134436605220904112976181830667719194987401487083318674802988854162959819380836946082331599524262746254371194008455335100511575399853393989205829635294039833907532465672925195976719223986566158148447432642878876393174990625370999994436703961567355244807342"
   "69520641509898359015919277259612693573103507245475766862906920585023739066501517668430030086321626352865287081845785302216800971241480075909946702545978734966313183497339950198090782159728060732277203943611763413586858764551979183951623117755116853112134485823173566471719295417944467155436759146240378961164970793485454582634507950507263335329774425734366159044434769283194752971059777295937298636851773307465268720002428067710554915705131921586485915706058588546940127798302503433978964008455133493"
   "81377493873988568454747394555631322019995974349284897738681796214043648315850753309356488047165462695097002375464907491747011057687497039054057966902781968701738366805404618496287646992501561538984505139538837143696192930809348816477556056131508918563803091019843961483373516766792142628561297000391741657862041350478818272826493800507083056161770890242060904410455232572423354649059801137081343658420929866819323305436461528792337009143429528398908615401610285467879096995482235819088087170007172084"
   "19200822408181677356654363526604525957125493746470602005173044123902131293500348212963003814048430419681143694193562181860541774246853516513052798389919270163992446463727779357701165620885423995382643338114200119079737025997410561889640258558778992205266673912623015354974700417348049919786128796213631654305377502373403534621197573237204087833472227750274081961941830295476410425392309508980002436399077209866428844044550242714253720047714826171774213541484239371095511846919312369075331533699620236"
   "82029292972878352449179621419103939233902274750203384387435157086116363044339285082959598074623165693670827043027578937417097999903630537953141651179848326672037726918504834056001686619269163272418061895808182857874697372932191591163332075685243434819720224387476770995163639439295727754087687364735109641505765032061100086727178993910597551270640961004749914285104529318068253424299184344438067191184804803834191597230187778600478472083276828758014931933417546242450249754712385119736481807160467673"
   "70904104094257523331122117513138127146204955443643419997609850963761672445279987087328471556603601094954443309436688190549302755292682697985196968743116624708516670254571842252732826810514201166286267256453342635852209107896954083931866292261343402422275337258151166893091264000780411251304831871670820972593920181986511958104229235575222334489531671074281201465449355239054615343692684276140882286660146413938639484415391107647591465166673502924457584083168747424556139933572957677237294159113337504"
   "42E3663"
};

template <class T>
const T& test_control_value_sqrt_pi() { return T(); }

template <>
const big_float_type& test_control_value_sqrt_pi<big_float_type>()
{
  static const big_float_type val(str_control_sqrt_pi);

  return val;
}

template <class T>
const T& test_control_value_pisot() { return T(); }

template <>
const big_float_type& test_control_value_pisot<big_float_type>()
{
  static const big_float_type val(str_control_pisot);

  return val;
}

template <class T>
bool test_sqrt_pi(const T tol = std::numeric_limits<T>::epsilon() * 1000000U)
{
   using std::fabs;
   using std::sqrt;

   const T sqrt_pi = sqrt(boost::math::constants::pi<T>());

   const T closeness = fabs(1 - fabs(sqrt_pi / test_control_value_sqrt_pi<T>()));

   const bool result_is_ok = (closeness < tol);

   return result_is_ok;
}

template <class T>
bool test_pisot(const T tol = std::numeric_limits<T>::epsilon() * 1000000U)
{
   using std::cbrt;
   using std::fabs;
   using std::pow;
   using std::sqrt;

   const T term  = T(27U) + (sqrt(T(69)) * 3U);

   const T pisot = pow(cbrt(2 / term) + (cbrt(term / 2) / 3), 30000U);

   const T closeness = fabs(1 - fabs(pisot / test_control_value_pisot<T>()));

   const bool result_is_ok = (closeness < tol);

   return result_is_ok;
}

template <class T>
bool test_sqrt_pi_modify_one_digit_to_fail(const T tol = std::numeric_limits<T>::epsilon() * 1000000U)
{
   using std::fabs;
   using std::sqrt;

   const T sqrt_pi = sqrt(boost::math::constants::pi<T>());

   std::string str_wrong(str_control_sqrt_pi);

   // We will now purposefully modify the string at position 9502.
   // This is the character '8' at the beginning of the final complete
   // line of the initialization of the string str_control_sqrt_pi.
   if(str_wrong[9502U] == (char) '8')
   {
     str_wrong[9502U] = (char) '9';
   }

   const T control(str_wrong);

   const T closeness = fabs(1 - fabs(sqrt_pi / control));

   // Ensure that the answer is wrong (which is the correct behavior).
   const bool wrong_result_is_ok = (closeness > tol);

   return wrong_result_is_ok;
}

int main()
{
    #if defined(TEST_CPP_DEC_FLOAT)
    const big_float_type local_tol = std::numeric_limits<big_float_type>::epsilon() * 100U;
    #elif defined(TEST_CPP_BIN_FLOAT)
    const big_float_type local_tol = std::numeric_limits<big_float_type>::epsilon() * 1000000U;
    #endif

   {
      const bool result_sqrt_pi_is_ok = test_sqrt_pi<big_float_type>(local_tol);
      BOOST_CHECK_EQUAL(result_sqrt_pi_is_ok, true);
   }

   {
      const bool result_pisot_is_ok = test_pisot<big_float_type>(local_tol);
      BOOST_CHECK_EQUAL(result_pisot_is_ok, true);
   }

   {
      const bool result_sqrt_pi_modify_one_digit_to_fail_is_ok = test_sqrt_pi_modify_one_digit_to_fail<big_float_type>(local_tol);
      BOOST_CHECK_EQUAL(result_sqrt_pi_modify_one_digit_to_fail_is_ok, true);
   }

   return boost::report_errors();
}
